package de.tub.bazinga.player;

import java.awt.geom.Point2D;
import java.util.HashSet;
import java.util.Set;

import processing.core.PApplet;
import processing.core.PVector;
import SimpleOpenNI.SimpleOpenNI;
import SimpleOpenNI.XnSkeletonJointPosition;
import de.tub.bazinga.event.HitEvent;
import de.tub.bazinga.eventbus.DefaultEventBus;
import de.tub.bazinga.object.Circle;
import de.tub.bazinga.object.Color;
import de.tub.bazinga.object.Target;
import de.tub.bazinga.object.TargetDefinition;
import de.tub.bazinga.object.interf.Drawable;

public class Player implements Drawable {

	public final static Color INACTIVE_COLOR = new Color(255, 255, 255, 255);

	private final SimpleOpenNI context;

	private final XnSkeletonJointPosition headPosition;
	private final XnSkeletonJointPosition leftHandPosition;
	private final XnSkeletonJointPosition rightHandPosition;
	private final XnSkeletonJointPosition leftFootPosition;
	private final XnSkeletonJointPosition rightFootPosition;

	private final int userID;
	private Circle circle;
	private final Set<Target> targets = new HashSet<Target>();
	private int points = 0;

	// private final IAudioManager audioManager =
	// AudioManagerImpl.getInstance();

	private Color playerColor;

	public Player(int userID, SimpleOpenNI context) {
		this.userID = userID;
		this.context = context;

		playerColor = INACTIVE_COLOR;

		headPosition = new XnSkeletonJointPosition();
		leftHandPosition = new XnSkeletonJointPosition();
		rightHandPosition = new XnSkeletonJointPosition();
		leftFootPosition = new XnSkeletonJointPosition();
		rightFootPosition = new XnSkeletonJointPosition();

		updateCirclePosition();
	}

	private void updateJointPositions() {

		extractPosition(context, SimpleOpenNI.SKEL_HEAD, headPosition);
		extractPosition(context, SimpleOpenNI.SKEL_LEFT_HAND, leftHandPosition);
		extractPosition(context, SimpleOpenNI.SKEL_RIGHT_HAND,
				rightHandPosition);
		extractPosition(context, SimpleOpenNI.SKEL_LEFT_FOOT, leftFootPosition);
		extractPosition(context, SimpleOpenNI.SKEL_RIGHT_FOOT,
				rightFootPosition);
	}

	public Point2D getBodyCenter() {
		PVector center = new PVector();
		context.getCoM(getUserID(), center);

		context.convertRealWorldToProjective(center, center);

		return new Point2D.Float(center.x, center.y);
	}

	public double getBodyHeight() {

		XnSkeletonJointPosition headPosition = new XnSkeletonJointPosition();
		extractPosition(context, SimpleOpenNI.SKEL_HEAD, headPosition);

		XnSkeletonJointPosition leftAnkle = new XnSkeletonJointPosition();
		extractPosition(context, SimpleOpenNI.SKEL_LEFT_ANKLE, leftAnkle);

		XnSkeletonJointPosition rightAnkle = new XnSkeletonJointPosition();
		extractPosition(context, SimpleOpenNI.SKEL_LEFT_ANKLE, rightAnkle);

		double topY = headPosition.getPosition().getY();

		double bottomY = leftAnkle.getPosition().getY();
		if (bottomY < rightAnkle.getPosition().getY()) {
			bottomY = rightAnkle.getPosition().getY();
		}

		if (Double.isNaN(bottomY)) {
			bottomY = context.depthHeight();
		}

		return topY - bottomY;
	}

	public void updateCirclePosition() {
		Point2D bodyCenter = this.getBodyCenter();
		double radius = this.getCircleHeight() / 2;

		if (circle == null) {
			circle = new Circle(bodyCenter, (int) radius);
			circle.setStrokeColor(playerColor);
		} else if (Double.isNaN(circle.getPosition().getX())
				|| Double.isNaN(circle.getPosition().getY())) {
			circle.setPosition(bodyCenter);
			circle.setRadius((int) radius);
		} else {
			double relation = 0.8;
			circle.setPosition(new Point2D.Double(circle.getPosition().getX()
					* relation + bodyCenter.getX() * (1 - relation), circle
					.getPosition().getY()
					* relation
					+ bodyCenter.getY()
					* (1 - relation)));
			circle.setRadius((int) (circle.getRadius() * relation + radius
					* (1 - relation)));
		}

	}

	private void extractPosition(SimpleOpenNI context, int skeleton,
			XnSkeletonJointPosition jointPosition) {
		context.getJointPositionSkeleton(userID, skeleton, jointPosition);
		context.convertRealWorldToProjective(jointPosition.getPosition(),
				jointPosition.getPosition());

	}

	@Override
	public void draw(PApplet window) {
		updateJointPositions();

		colorizePlayerBody(window);

		getCircle().draw(window);
		Set<Target> hitTargets = new HashSet<Target>();
		for (Target t : targets) {

			if (t.isHit()) {
				hitTargets.add(t);
			}

			t.draw(window);
		}

		// drawBodyCenter(window);
		drawScore(window);

		if (hitTargets.size() > 0) {
			DefaultEventBus.getInstance().fireEvent(
					new HitEvent(this, hitTargets));

		}
	}

	private void colorizePlayerBody(PApplet window) {
		window.loadPixels();
		int color = window.color(playerColor.getR(), playerColor.getG(),
				playerColor.getB(), playerColor.getA());
		int[] usersPixels = context.getUsersPixels(SimpleOpenNI.USERS_ALL);
		for (int y = 0; y < context.depthHeight(); ++y) {
			for (int x = 0; x < context.depthWidth(); ++x) {
				int index = x + y * context.depthWidth();
				if (usersPixels[index] == getUserID()) {
					window.pixels[index] = color;
				}
			}
		}
		window.updatePixels();
	}

	// private void drawBodyCenter(PApplet window) {
	// Point2D center = getBodyCenter();
	//
	// window.stroke(0, 255, 0, 255);
	// window.line((int) (center.getX() - 10), (int) center.getY(),
	// (int) (center.getX() + 10), (int) center.getY());
	// window.line((int) (center.getX()), (int) (center.getY() - 10),
	// (int) (center.getX()), (int) (center.getY() + 10));
	//
	// }

	private void drawScore(PApplet window) {
		if (targets.isEmpty()) {
			return;
		}

		window.fill(245);
		window.textSize(100);

		Point2D center = getBodyCenter();
		window.text(this.points, (float) center.getX() - 25,
				(float) center.getY());
	}

	public double getCircleHeight() {
		return getBodyHeight();
	}

	public void removeTargets() {
		targets.clear();
	}

	public Set<Target> getTargets() {
		return targets;
	}

	public void addTargetByDefinition(TargetDefinition td) {
		Target newTarget = new Target(td, this);
		this.targets.add(newTarget);
	}

	public void addTargetsByDefinition(Set<TargetDefinition> tds) {
		for (TargetDefinition td : tds) {
			addTargetByDefinition(td);
		}
	}

	public int getPoints() {
		return points;
	}

	public void addPoint() {
		points++;
	}

	public void addPoints(int points) {
		this.points += points;
	}

	public void resetPoints() {
		this.points = 0;
	}

	public Circle getCircle() {
		return circle;
	}

	public XnSkeletonJointPosition getHeadPosition() {
		return headPosition;
	}

	public XnSkeletonJointPosition getLeftHandPosition() {
		return leftHandPosition;
	}

	public XnSkeletonJointPosition getRightHandPosition() {
		return rightHandPosition;
	}

	public XnSkeletonJointPosition getLeftFootPosition() {
		return leftFootPosition;
	}

	public XnSkeletonJointPosition getRightFootPosition() {
		return rightFootPosition;
	}

	public int getUserID() {
		return userID;
	}

	public Color getColor() {
		return playerColor;
	}

	public void setColor(Color color) {
		playerColor = color;
		if (circle != null) {
			circle.setStrokeColor(playerColor);
		}
	}

	public SimpleOpenNI getContext() {
		return context;
	}
}
